package models

import (
	"encoding/json"
	"fmt"
	"github.com/go-gorp/gorp"
	"github.com/orcaman/concurrent-map"
	"github.com/revel/revel"
	"golang.org/x/crypto/bcrypt"
	"io"
	"regexp"
	"strconv"
	"strings"
)

var (
	ROOT     = 3
	CUSTOMER = 0
	METCHANT = 1
)

var users = cmap.New()

type User struct {
	UserId             int
	Name               string
	Username, Password string
	HashedPassword     []byte
	Wxopenid           string
	Tel                string
	Addr               string
	Alteraddrs         string
	Role               int //角色 普通角色  0普通用户  1 商铺用户 3管理用户

}

func (u *User) UseridStr() string {
	return strconv.Itoa(u.UserId)
}

func (u *User) String() string {
	return fmt.Sprintf("User(%s)", u.Username)
}

var userRegex = regexp.MustCompile("^\\w*$")

func (user *User) Validate(v *revel.Validation) {
	v.Check(user.Username,
		revel.Required{},
		revel.MaxSize{15},
		revel.MinSize{4},
		revel.Match{userRegex},
	)

	ValidatePassword(v, user.Password).
		Key("user.Password")

	v.Check(user.Name,
		revel.Required{},
		revel.MaxSize{100},
	)
}

func ValidatePassword(v *revel.Validation, password string) *revel.ValidationResult {
	return v.Check(password,
		revel.Required{},
		revel.MaxSize{15},
		revel.MinSize{5},
	)
}

/**
* 用户信息更新
 */

func UpdateUserRole(userid string, role int, txn *gorp.Transaction) error {
	user := GetUserByUserId(userid)
	user.Role = role
	if user != nil {
		txn.Update(user)
	}
	return nil
}

/*
* 网页授权接口凭证
 */
type WebAccessTokenObj struct {
	Access_token  string
	Expires_in    int
	Refresh_token string
	Openid        string
	Scope         string
	Errcode       int
	Errmsg        string
}

func (w *WebAccessTokenObj) String() string {
	return w.Access_token + " 123\t" +
		w.Refresh_token + "123\t" +
		w.Openid + "\t" +
		w.Scope
}

func ParseWxWebAccessToken(jstr string) *WebAccessTokenObj {
	dec := json.NewDecoder(strings.NewReader(jstr))
	obj := &WebAccessTokenObj{}
	if err := dec.Decode(obj); err == io.EOF {
		return nil
	} else if err != nil {
		revel.ERROR.Println(err.Error())
		return nil
	}

	if obj.Errcode != 0 {
		revel.ERROR.Printf("get weixin web access_token error %d, msg: %s", obj.Errcode, jstr)
		return nil
	}

	return obj
}

//根据用户userid 获取User
func GetUserByUserId(userid string) *User {
	//首先查询缓存中的数据
	if user, ok := users.Get(userid); ok {
		return user.(*User)
	} else {
		//查询数据库 不需要使用事物
		res, err := Dbm.Select(User{}, `select * from user where UserId = ?`, userid)
		if err != nil {
			revel.ERROR.Println(err.Error())
			return nil
		}
		if len(res) == 0 {
			revel.ERROR.Printf("select * from user where Username = %s returns no user\n", userid)
			return nil
		}
		user := res[0].(*User)
		users.Set(user.UseridStr(), user)
		return user
	}
}

func GetUserByUsername(username string) *User {
	users, err := Dbm.Select(User{},
		`select * from user where Username = ?`,
		username)
	if err != nil {
		revel.ERROR.Printf("get user by name %s error %s", username, err.Error())
		return nil
	}
	if len(users) == 0 {
		revel.ERROR.Printf("get no user by name %s", username)
		return nil
	}
	return users[0].(*User)
}

func CreateUserByUsernameAndPassword(user User) *User {
	user.HashedPassword, _ = bcrypt.GenerateFromPassword(
		[]byte(user.Password), bcrypt.DefaultCost)
	err := Dbm.Insert(&user)

	if err != nil {
		revel.ERROR.Printf("insert user with username %s error %s", user.Username, err.Error())
	}

	users, err := Dbm.Select(User{},
		`select * from user where username = ?`,
		user.Username)
	if err != nil {
		revel.ERROR.Printf("get user by username %s error %s", user.Username, err.Error())
	}
	if len(users) == 0 {
		revel.ERROR.Printf("create user by username %s", user.Username)
		return nil
	}
	return users[0].(*User)
}

/*
	根据用户的weixinopenid 获取用户
	当用户需要用wxopenid进行获取的时候 基本上就是第一次关注的时候
*/
func GetOrCreateWxUser(wat *WebAccessTokenObj) *User {
	wxopenid := wat.Openid
	users, err := Dbm.Select(User{},
		`select * from user where Wxopenid = ?`,
		wxopenid)
	if err != nil {
		revel.ERROR.Printf("get user by wxopenid error %s", err.Error())
	}
	if len(users) == 0 {
		revel.INFO.Printf("no wxopenid user %s, create it", wxopenid)
		return CreatWxUser(wxopenid)
	}
	return users[0].(*User)
}

//创建wx用户
func CreatWxUser(wxid string) *User {
	user := &User{Wxopenid: wxid}
	if err := Dbm.Insert(user); err != nil {
		revel.ERROR.Printf("insert user with wxopenid error %s", err.Error())
		return nil
	}

	users, err := Dbm.Select(User{},
		`select * from user where Wxopenid = ?`,
		wxid)
	if err != nil {
		revel.ERROR.Printf("get user by wxopenid error %s", err.Error())
	}
	if len(users) == 0 {
		revel.ERROR.Printf("create user by wxopenid error %s", err.Error())
		return nil
	}
	return users[0].(*User)
}
